﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Threading;
using System.Text;
using System.Net.Sockets;
using System.Windows.Threading;

namespace PTestClient
{
    public partial class MainPage : UserControl
    {
        #region Fields

        //定义一个可在全局使用的Socket
        System.Net.Sockets.Socket socket;

        //定义一个同步上下文类，用来将子线程的操作调度到主线程上以可控制UI属性。
        SynchronizationContext syn;

        #endregion Fields

        #region Constructor and initialize controls
 
        public MainPage()
        {
            InitializeComponent();
            InitializeControls();
            SendRequestMessage(this.GetCaseListCommand);

            #region Check is server busy

            DispatcherTimer timer = new DispatcherTimer();
            timer.Interval = TimeSpan.FromSeconds(2);
            timer.Tick += (sender, e) =>
            {
                // send is server busy request
                SendRequestMessage(CommandNames.ISSERVEBUSY.ToString());
            };

            timer.Start();

            #endregion Check is server busy
        }

        private void InitializeControls()
        {
            cbAssemblyType.SelectedIndex = 0;
            btnDoTest.IsEnabled = false;
        }

        #endregion Constructor and initialize controls

        #region Properties

        private string GetCaseListCommand
        {
            get
            {
                if (cbAssemblyType.SelectedIndex == 0)
                {
                    return CommandNames.SLTCASELIST.ToString();
                }
                else if (cbAssemblyType.SelectedIndex == 1)
                {
                    return CommandNames.WPFCASELIST.ToString();
                }
                else
                {
                    throw new IndexOutOfRangeException();
                }
            }
        }

        private AssemblyType AssemblyType
        {
            get
            {
                if (cbAssemblyType.SelectedIndex == 0)
                {
                    return AssemblyType.SLT;
                }
                else if (cbAssemblyType.SelectedIndex == 1)
                {
                    return AssemblyType.WPF;
                }
                else
                {
                    throw new IndexOutOfRangeException();
                }
            }
        }

        #endregion Properties

        #region Event Handlers

        private void BtnDoTest_Click(object sender, RoutedEventArgs e)
        {
            // get case list
            string caseList = GetRunCaseList();
            if (caseList == null)
            {
                MessageBox.Show("Please select some cases!");
                return;
            }
            // send message
            SendRequestMessage(caseList);
        }

        private void BtnReloadAssembly_Click(object sender, RoutedEventArgs e)
        {
            tvCases.Items.Clear();
            SendRequestMessage(this.GetCaseListCommand);
        }

        #endregion Event Handlers

        #region Socket operation

        private void socketArgs_Completed(object sender, SocketAsyncEventArgs e)
        {
            //当任何一个Socket动作完成，都回调该函数，然后对LastOperation进行判断后继续执行相应的部分
            switch (e.LastOperation)
            {
                case SocketAsyncOperation.Connect:
                    ProcessConnect(e);
                    break;               
                case SocketAsyncOperation.Send:
                    ProcessSend(e);
                    break;
                case SocketAsyncOperation.Receive:
                    ProcessReceive(e);
                    break;
            }
        }

        //将数据放入buffer并进行异步发送
        private void ProcessConnect(SocketAsyncEventArgs e)
        {
            //当连接成功后，获取Socket参数 e传递过来的用户标识（也就是本示例中用户输入的字符串转换的Byte字节数组）
            byte[] bytes = (byte[])e.UserToken;

            //设置Socket参数的缓冲区参数，将我们的字节数组设置为Socket的缓冲区。
            e.SetBuffer(bytes, 0, bytes.Length);

            //发送数据
            socket.SendAsync(e);
        }

        //发送完成后，执行等待接收服务器发回的数据
        private void ProcessSend(SocketAsyncEventArgs e)
        {
            //定义个空的字节数组，设置好其大小
            //byte[] bytes = new byte[1024];
            byte[] bytes = new byte[40000];
            //将前面定义字节数组设置成缓冲区
            e.SetBuffer(bytes, 0, bytes.Length);
            //执行异步接收
            socket.ReceiveAsync(e);
        }

        private void ProcessReceive(SocketAsyncEventArgs e)
        {
            //在执行好接收后，本地SOCKET的缓冲区就会被服务器发送的数据填充。
            //显示下信息，当然也是用同步上下文的方式，在显示信息的时候，就直接将缓冲区的字节数组转换成字符串。
            string s = Encoding.UTF8.GetString(e.Buffer, 0, e.Buffer.Length);
            HandleReceivedMessage(s);
            
            //关闭Socket连接
            socket.Close();
        }

        private void HandleReceivedMessage(string message)
        {
            string requestCommand = message.Substring(0, 11);
            string messageBody = message.Substring(11, message.Length - 11);
            if (requestCommand == CommandNames.SLTCASELIST.ToString() ||
                requestCommand == CommandNames.WPFCASELIST.ToString())
            {
                BindingCaseListtoViewTree(messageBody);
            }
            else if (requestCommand == CommandNames.SERVERBUSYY.ToString())
            {
                SetIsServerBusy(true);
            }
            else if (requestCommand == CommandNames.SERVEUNBUSY.ToString())
            {
                SetIsServerBusy(false);
            }
        }

        private void SetIsServerBusy(bool isServerBusy)
        {
            btnDoTest.Dispatcher.BeginInvoke((Action)(() =>
            {
                btnDoTest.IsEnabled = !isServerBusy;
            }));
            
        }

        private void SendRequestMessage(string requestMessage)
        {
            //定义一个字节数组，并将文本框的的类容转换为字节数组后存入
            byte[] bytes = Encoding.UTF8.GetBytes(requestMessage);

            //将同步上下文设置在当前上下文（线程，主线程，可控制UI的）
            syn = SynchronizationContext.Current;

            //为socket创建示例，并设置相关属性。
            socket = new System.Net.Sockets.Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

            //定义并实例一个Socket参数
            SocketAsyncEventArgs socketArgs = new SocketAsyncEventArgs();
            socketArgs.RemoteEndPoint = new IPEndPoint(IPAddress.Parse("10.32.1.219"), 4502);
            socketArgs.Completed += new EventHandler<SocketAsyncEventArgs>(socketArgs_Completed);
            socketArgs.UserToken = bytes;

            //执行连接。
            socket.ConnectAsync(socketArgs);
        }

        #endregion Socket operation

        #region ViewTree operation

        private void BindingCaseListtoViewTree(string testStr)
        {
            //string testStr = "Assembly!ass1;Class!class1;Method!method1;Method!method2;Method!method3;Class!class2;Method!method1;Method!method2;Method!method3;";
            string[] items = testStr.Split(';');

            // get first string, it must be a assembly
            string firstString = items[0];
            string[] assemblyValue = firstString.Split('!');
            tvCases.Dispatcher.BeginInvoke((Action)(() =>
            {
                TreeViewItem root = new TreeViewItem();
                if (assemblyValue[0] == "Assembly")
                {
                    string[] lastName = assemblyValue[1].Split('.');

                    root.Tag = false;
                    CheckBox cbAssembly = new CheckBox();
                    // todo need change.
                    cbAssembly.Content = "Assembly";
                    cbAssembly.Checked += new RoutedEventHandler(checkBox_Checked);
                    cbAssembly.Unchecked += new RoutedEventHandler(checkBox_Unchecked);
                    cbAssembly.Tag = root;
                    root.Header = cbAssembly;
                    root.DataContext = assemblyValue[1];
                    tvCases.Items.Add(root);
                }

                TreeViewItem classView = null;
                for (int i = 1; i < items.Length - 1; i++)
                {
                    string item = items[i];
                    string[] keyValues = item.Split('!');

                    string[] lastNames = keyValues[1].Split('.');
                    string lastName = lastNames[lastNames.Length - 1];
                    if (keyValues[0] == "Class")
                    {
                        // add a class node for pro assembly node
                        classView = new TreeViewItem();

                        classView.Tag = false;
                        CheckBox cbClass = new CheckBox();
                        cbClass.Content = lastName;
                        cbClass.Checked += new RoutedEventHandler(checkBox_Checked);
                        cbClass.Unchecked += new RoutedEventHandler(checkBox_Unchecked);
                        // classView.Header = keyValues[1];
                        cbClass.Tag = classView;
                        classView.Header = cbClass;
                        classView.DataContext = keyValues[1];
                        root.Items.Add(classView);
                    }

                    if (keyValues[0] == "Method")
                    {
                        // add a class node for pro assembly node
                        TreeViewItem methodView = new TreeViewItem();
                        methodView.Tag = false;
                        CheckBox cbMethod = new CheckBox();
                        cbMethod.Content = lastName;
                        cbMethod.Checked += new RoutedEventHandler(checkBox_Checked);
                        cbMethod.Unchecked += new RoutedEventHandler(checkBox_Unchecked);
                        // classView.Header = keyValues[1];
                        cbMethod.Tag = methodView;
                        methodView.Header = cbMethod;

                        // methodView.Header = keyValues[1];
                        methodView.Header = cbMethod;
                        methodView.DataContext = keyValues[1];
                        classView.Items.Add(methodView);
                    }
                }
            }));
            // tvCases 
        }

        private void checkBox_Unchecked(object sender, RoutedEventArgs e)
        {
            CheckBox cb = (CheckBox)sender;
            var owner = cb.Tag as TreeViewItem;
            owner.Tag = false;
            // handle childen
            if (owner.Items.Count > 0)
            {
                SetAllChildNodeValue(owner, false);
            }
            CheckLayerGetResult(owner);
        }

        private void checkBox_Checked(object sender, RoutedEventArgs e)
        {
            CheckBox cb = (CheckBox)sender;
            var owner = cb.Tag as TreeViewItem;
            owner.Tag = true;
            // handle childen
            if (owner.Items.Count > 0)
            {
                SetAllChildNodeValue(owner, true);
            }
            CheckLayerGetResult(owner);
        }

        // set value from child to father
        private void SetNodeValue(TreeViewItem currentNode, bool? value)
        {
            currentNode.Tag = value ?? null;
            CheckBox cb = (CheckBox)currentNode.Header;
            cb.IsChecked = value;
        }

        // set value from father to child
        private void SetNodeValue(TreeViewItem currentNode, bool value)
        {
            currentNode.Tag = value;
            CheckBox cb = (CheckBox)currentNode.Header;
            cb.IsChecked = value;
        }

        private void CheckLayerGetResult(TreeViewItem currentNode)
        {
            bool? value = null;
            TreeViewItem father = null;
            if (currentNode.Parent is TreeViewItem)
            {
                father = currentNode.Parent as TreeViewItem;
            }
            else
            {
                return;
            }

            if (currentNode.Tag == null)
            {
                // set all parent node to null.
                value = null;
                //father.Tag = null;
                SetNodeValue(father, value);
                CheckLayerGetResult(father);
            }
            else if ((bool)currentNode.Tag == true)
            {
                bool allTrue = true;
                foreach (TreeViewItem item in father.Items)
                {
                    if (item.Tag == null || (bool)item.Tag == false)
                    {
                        value = null;
                        //father.Tag = null;
                        SetNodeValue(father, value);
                        allTrue = false;
                        break;
                    }
                }
                if (allTrue)
                {
                    value = true;
                    //father.Tag = null;
                    SetNodeValue(father, value);
                }
                CheckLayerGetResult(father);
            }
            else if ((bool)currentNode.Tag == false)
            {
                // check brother
                bool allFalse = true;
                foreach (TreeViewItem item in father.Items)
                {
                    if (item.Tag == null || (bool)item.Tag == true)
                    {
                        value = null;
                        //father.Tag = null;
                        SetNodeValue(father, value);
                        allFalse = false;
                        break;
                    }
                }
                if (allFalse)
                {
                    value = false;
                    //father.Tag = null;
                    SetNodeValue(father, value);
                }
                CheckLayerGetResult(father);
            }
        }

        private void SetAllChildNodeValue(TreeViewItem currentNode, bool value)
        {
            foreach (TreeViewItem item in currentNode.Items)
            {
                SetNodeValue(item, value);
                SetAllChildNodeValue(item, value);
            }
        }

        private string GetRunCaseList()
        {
            // check some case be selected.
            TreeViewItem root = tvCases.Items[0] as TreeViewItem;
            if (!(root.Tag == null || (bool)root.Tag == true))
            {
                return null;
            }

            StringBuilder sb = new StringBuilder();
            if (this.AssemblyType == PTestClient.AssemblyType.SLT)
            {
                sb.Append(CommandNames.DOSLTPFTEST.ToString() + ";");
            }
            else
            {
                sb.Append(CommandNames.DOWPFPFTEST.ToString() + ";");
            }

            foreach (TreeViewItem item in (tvCases.Items[0] as TreeViewItem).Items)
            {
                if (item.Tag == null || (bool)item.Tag == true)
                {
                    sb.Append(item.DataContext.ToString() + ";");
                    foreach (TreeViewItem ite in item.Items)
                    {
                        if (ite.Tag != null && (bool)ite.Tag == true)
                        {
                            sb.Append(ite.DataContext.ToString() + ";");
                        }
                    }
                }
            }
            sb.Append(sb.Length.ToString() + ";");
            return sb.ToString();
        }

        #endregion ViewTree operation
    }
}
